package org.yanghaokun.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.yanghaokun.basic.exception.BusinessException;
import org.yanghaokun.basic.service.impl.BaseServiceImpl;
import org.yanghaokun.basic.util.AjaxResult;
import org.yanghaokun.basic.util.HttpClientUtils;
import org.yanghaokun.basic.util.MD5Utils;
import org.yanghaokun.basic.util.StrUtils;
import org.yanghaokun.user.constant.UserConstant;
import org.yanghaokun.user.constant.WxConstants;
import org.yanghaokun.user.domain.LoginInfo;
import org.yanghaokun.user.domain.User;
import org.yanghaokun.user.domain.WxUser;
import org.yanghaokun.user.dto.LoginDto;
import org.yanghaokun.user.mapper.LoginInfoMapper;
import org.yanghaokun.user.mapper.UserMapper;
import org.yanghaokun.user.mapper.WxUserMapper;
import org.yanghaokun.user.service.ILoginInfoService;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginInfoServiceImpl extends BaseServiceImpl<LoginInfo> implements ILoginInfoService {

    @Autowired
    private LoginInfoMapper loginInfoMapper;
    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    public AjaxResult loginAccount(LoginDto loginDto) {
        //非空判断
        if(StringUtils.isEmpty(loginDto.getUsername()) || StringUtils.isEmpty(loginDto.getPassword())
                || StringUtils.isEmpty(loginDto.getType())) {
            throw new BusinessException("不能空白项");
        }
        //判断用户是否存在
        //根据封装的loginDto信息去数据库查找用户数据找不到择不粗在该用户
        LoginInfo loginInfo = loginInfoMapper.findByAccount(loginDto);
        if (loginInfo == null){
            throw new BusinessException("该用户不存在");
        }
        //判断用户状态是否被禁用
        if (loginInfo.getDisable() == -1) {
            throw new BusinessException("账号已被冻结，请联系客服处理");
        }
        //校验密码
        String salt = loginInfo.getSalt();
        String dbPwd = loginInfo.getPassword();
        String inputPwd = MD5Utils.pwdByMd5(loginDto.getPassword() + salt);
        if (!inputPwd.equals(dbPwd)){
            throw new BusinessException("用户名或密码不正确");
        }
        //记录loginInfo到redis，key为token
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);
        //登录成功需要返回token和loginInfo
        Map<String,Object> result = new HashMap<>();
        result.put("token",token);
        //为了安全，不讲盐值和密码传到前台
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        result.put("loginInfo",loginInfo);
        //返回AjaxResult,返回给前台
        return AjaxResult.me().setResultObj(result);
    }

    @Autowired
    private WxUserMapper wxUserMapper;
    @Override
    public AjaxResult loginWechat(Map<String, String> prams) {
        //这时前端传递了一些参数回来就是为了拿到token的，窜回来了code还有binderUrl
        //取出code
        String code = prams.get("code");
        //取出bindeUrl
        String binderUrl = prams.get("binderUrl");
        //我们那从前台取到的code去获取access_token，这是需要发送一个get请求，将网址中的一些参数替换
        //因为发送请求的网址对于我们来说就是固定的，所以把他做成一个常量
        String getACKurl = WxConstants.GET_ACK_URL
                .replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code);
        //接下来我们发送请求获得一个字符窜
        String jsonStr = HttpClientUtils.httpGet(getACKurl);
        //我们如果去要从这个字符窜拿到access_token，还有openId我们就需要先转化为JSON对象,阿里巴巴写的，所以见名知意
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        //拿到access_token和openId
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");

        //看是否已经关联了用户信息
        //根据openId来看是否能查到信息，这个oprnId就属于微信的唯一标识
        WxUser wxUser = wxUserMapper.findByOpenId(openid);
        if (wxUser != null && wxUser.getUser_id()!=null) {
            //查寻loginInfo，为了后面存放起来实现关联
            LoginInfo loginInfo = loginInfoMapper.findByUserId(wxUser.getUser_id());
            //关联直接免密登陆
            String token = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);
            Map<String,Object> result = new HashMap<>();
            result.put("token",token);
            loginInfo.setSalt(null);
            loginInfo.setPassword(null);
            result.put("loginInfo",loginInfo);
            return AjaxResult.me().setResultObj(result);
        }
        else {
            //没有关联就绑定
            //将获取到的access_token和openId传递到前台连着绑定路径
            binderUrl = binderUrl + "?accessToken=" + access_token + "&openId=" + openid;
            return AjaxResult.me().setSuccess(false).setResultObj(binderUrl);
        }
    }

    @Autowired
    private UserMapper userMapper;
    //前台输入手机号是否有账号，如果有创建wxUser帮上就ok，如果没有创建账号在绑定
    @Override
    public AjaxResult binderWechat(Map<String, String> prams) {
        //先拿到从前台传过来的参数
        String phone = prams.get("phone");
        String verifyCode = prams.get("verifyCode");
        String accessToken = prams.get("accessToken");
        String openId = prams.get("openId");

        //做验证码比对
        Object codeObj = redisTemplate.opsForValue().get(UserConstant.BINDER_PREFIX + phone);
        if (codeObj == null) {
            return AjaxResult.me().setMessage("请重新获取验证码！");
        }else {
            String codeStr = (String) codeObj;
            //从中获取存在redis中的验证码
            String code = codeStr.split(":")[0];
            if (!verifyCode.equals(code)) {
                return AjaxResult.me().setMessage("请输入正确验证码！");
            }
        }

        //获取微信用户信息
        String getUserurl = WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        String jsonStr = HttpClientUtils.httpGet(getUserurl);

        //通过电话和type获取用户登录信息
        //之前我们查询用户信息的时候将字段条件封装到了LoginDto中，现在我们继续引用这个对象将我们拿到的参数传进去来查询就好
        LoginDto loginDto = new LoginDto();
        loginDto.setUsername(phone);
        //因为我们是查询用户而不是员工
        loginDto.setType(1);
        LoginInfo loginInfo = loginInfoMapper.findByphone(loginDto);
        //如果用户信息不存在
        User user = null;
        if (loginInfo == null) {
            //创建loginInfo
            //首先我们先将微信用户转换为用户
            user = wxUser2User(phone);
            //再将用户转换为loginInfo
            loginInfo = user2LoginInfo(user);
            //最后保存
            loginInfoMapper.save(loginInfo);
            //创建user都保存,先保存的loginInfo我们在这里设置进去我们可以拿到loginInfo的自增id在User里面就是loginInfo_id
            user.setLoginInfo(loginInfo);
            userMapper.save(user);
        }else {
            //如果用户存在
            user = userMapper.findByphone(phone);
        }
        //我们通过get请求拿到了用户的信息，然后转化为微信用户
        WxUser wxUser = wxuserjsonStr2WxUser(jsonStr);
        Long id = user.getId();
        //和用户绑定
        wxUser.setUser_id(user.getId());
        wxUser.getUser_id();
        //保存微信用户
        wxUserMapper.save(wxUser);
        //都操作完成直接绵密登录即可
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);
        Map<String,Object> result = new HashMap<>();
        result.put("token",token);
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        result.put("loginInfo",loginInfo);
        return AjaxResult.me().setResultObj(result);
    }

    @Override
    public void sendSms(Map<String,String> params) {
        String phone = params.get("phone");
        String type = params.get("type");
        //先校验手机号是否位null
        if(!StringUtils.hasLength(phone)) {
            //没有输入手机号
            throw new BusinessException("请输入手机号");
        }
        //下面判断验证码的类型
        if ("register".equals(type)) {//注册
            //存在用户不能注册
            User user = userMapper.findByphone(phone);
            if (user != null) {
                throw new BusinessException("该用户已经存在");
            }
            //是注册我们就把标识设置为注册
            String businessKey = UserConstant.REDIS_PREFIX + phone;
            sendSmscode(businessKey);
        }else if ("binder".equals(type)) {
            String businessKey = UserConstant.BINDER_PREFIX + phone;
            sendSmscode(businessKey);
        }else if ("login".equals(type)) {
            String businessKey = UserConstant.LOGIN_PREFIX + phone;
            sendSmscode(businessKey);
        }
    }

    private void sendSmscode(String businessKey) {
        //发送验证码，先判断之前的验证码是否有效,因为验证码一般都是存在redis中所以我们先去看看是否为空,这里我们定义一个常量提高效率防止写错
        Object codeObj = redisTemplate
                .opsForValue().get(businessKey);
        String code = "";
        //我们先看代码是否有效
        if (codeObj != null) {
            //将获取到的对象转换成String类型，分割获取第二个也就是xxxx：time时间
            String codeStr = (String) codeObj;
            String time = codeStr.split(":")[1];
            //当前时间减去剩余的重发时间还小于重发时间就不行
            //没有超过就给出错误提示
            if (System.currentTimeMillis() - Long.valueOf(time) <= 1 * 60 * 1000) {
                throw new BusinessException("请勿重复发送验证码");
            }
            //超过，但是验证码没有失效我们就继续保存
            code = codeStr.split(":")[0];
        }else {//代码失效了我们就重新生成验证码
            code = StrUtils.getComplexRandomString(4);
            //将验证码和当前时间保存到redis
            Long currentMills = System.currentTimeMillis();
            redisTemplate.opsForValue().set(businessKey, code + ":" + currentMills , 3, TimeUnit.MINUTES);
        }
        //最后调用短信接口发送短信
//        SmsUtil.sendMsg(phone,"您的验证码为" + code + "请在三分钟内携码洗jio！！！");
        System.out.println("您的验证码为" + code + "请在三分钟内携码洗jio！！！");
    }

    private WxUser wxuserjsonStr2WxUser(String jsonStr) {
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        WxUser wxUser = new WxUser();
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setAddress(null);
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        return wxUser;
    }

    private LoginInfo user2LoginInfo(User user) {
        LoginInfo loginInfo = new LoginInfo();
        BeanUtils.copyProperties(user,loginInfo);
        return loginInfo;
    }

    private User wxUser2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        user.setEmail(null);
        //设置随机盐值和密码
        String salt = UUID.randomUUID().toString();
        String pwd = MD5Utils.pwdByMd5(StrUtils.getComplexRandomString(6) + salt);
        user.setSalt(salt);
        user.setPassword(pwd);
        user.setState(1);
        user.setCreateTime(new Date());
        return user;
    }


}
